----------------------------------------------------------------------------------
--- Contains components for 'Memory' portion of pipeline 
--- AND
--- the MEN/WB register
----------------------------------------------------------------------------------

library IEEE;
Library UNISIM;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use UNISIM.vcomponents.all;
 

entity MEM_UNIT is
	PORT (
	
	CLK : in STD_LOGIC;
	
	--- CONTROL RELATED SIGNALS
		--- Used controls
	MEM_READ_IN : in STD_LOGIC;
	MEM_WRITE_IN : in STD_LOGIC;
		--- FLOW THROUGH CONTROL SIGNALS
	REG_WRITE_IN : in STD_LOGIC;
	MEM_TO_REG_IN : in STD_LOGIC;
	
	--- DATA RELATED SIGNALS
	ALU_IN : in STD_LOGIC_VECTOR(31 downto 0); -- Either the address for the memory (write or read) OR the data to be fedback in later stages
	WRITE_DATA : in STD_LOGIC_VECTOR(31 downto 0); -- Data to be written into the memory unit
	REG_DEST_IN : in STD_LOGIC_VECTOR(4 downto 0); -- Register destination flowing through
	
	--- FOR MEM_MEM HAZARDS
	MEM_WB_IS_READ_IN : in STD_LOGIC; -- If the previous instruction is a read from memory
	MEM_WB_IS_WRITE_TO_REG_IN : in STD_LOGIC; -- If the previous instruction will write back to a register
	WRITE_DATA_SRC_IN : in STD_LOGIC_VECTOR(4 downto 0); -- Register index of where the value we are writing from 
	MEM_WB_READ_TO_IN : in STD_LOGIC_VECTOR(4 downto 0); -- Register index of the previous instruction's destination registers
	MEM_WB_DATA_READ_IN : in STD_LOGIC_VECTOR(31 downto 0); -- The forwarded value from the MEM/WB register
	
	--- FOR UART INPUT
	U_OP1_IN : in STD_LOGIC_VECTOR(31 downto 0);
	U_OP2_IN : in STD_LOGIC_VECTOR(31 downto 0);
	
	------------------------------------------------------------------------------------------------------------------------
	--- OUTPUT SIGNALS ---
	------------------------------------------------------------------------------------------------------------------------
	--- CONTROL RELATED OUTPUT SIGNALS
	PC_SRC : out STD_LOGIC; -- 1 if we are required to branch, 0 if no branch
	REG_WRITE_OUT : out STD_LOGIC;
	MEM_TO_REG_OUT : out STD_LOGIC;
	
	--- DATA RELATED SIGNALS
	ALU_OUT : out STD_LOGIC_VECTOR(31 downto 0); -- output from ALU directly
	MEMORY_OUT : out STD_LOGIC_VECTOR(31 downto 0); -- output from memory unit, to be used for LW
	
	--- 
	REG_DEST_OUT : out STD_LOGIC_VECTOR(4 downto 0);
	
	---------------------------------
	-- UART PORTS
	---------------------------------
	U_RES1_OUT : out STD_LOGIC_VECTOR(31 downto 0);
	U_RES2_OUT : out STD_LOGIC_VECTOR(31 downto 0)
	
	);
end MEM_UNIT;

architecture Behavioral of MEM_UNIT is

	signal RAM_DATA : STD_LOGIC_VECTOR(31 downto 0); -- data from RAM
	signal UART_DATA : STD_LOGIC_VECTOR(31 downto 0); -- data from UART

	-- signal START_ADDR : STD_LOGIC_VECTOR(31 downto 0) := x"10010000"; ie
	-- signal END_ADDR : STD_LOGIC_VECTOR(31 downto 0) := x"10010FFF";
	
	--- Starting address 	0001 0000 0000 0001 0000 0000 0000 0000
	--- Ending address 		0001 0000 0000 0001 0000 0111 1111 1111
	
	--- Everything is word addressable ONLY! So only bits 10 downto 2 matter
	
	component DATA_RAM is
	PORT(
	CLK : in STD_LOGIC;
	DATA_IN : in STD_LOGIC_VECTOR(31 downto 0);
	ADDR_IN : in STD_LOGIC_VECTOR(8 downto 0); -- 12 bit address
	ENABLE : in STD_LOGIC;
	WRITE_ENABLE : in STD_LOGIC;
	DATA_OUT : out STD_LOGIC_VECTOR(31 downto 0)
	);

	end component;
	
	
	
	component MEM_MAPPED_IO is
	
	PORT (
	CLK : in STD_LOGIC;
	
	DATA_IN : in STD_LOGIC_VECTOR(31 downto 0);
	ADDR_IN : in STD_LOGIC_VECTOR(0 downto 0); -- Res1, Res0
	ENABLE : in STD_LOGIC;
	WRITE_ENABLE : in STD_LOGIC;
	RES1 : out STD_LOGIC_VECTOR(31 downto 0);
	RES2 : out STD_LOGIC_VECTOR(31 downto 0)
	
	);
	
	end component;
	
	component UART_IN is
	PORT (	
	clk : in STD_LOGIC;
	ADDR_IN : in STD_LOGIC_VECTOR(0 downto 0); -- From op1 (0), or op2(1)
	
	-- Input from UART module
	UART_IN1 : in STD_LOGIC_VECTOR(31 downto 0);
	UART_IN2 : in STD_LOGIC_VECTOR(31 downto 0);
	
	DATA_OUT : out STD_LOGIC_VECTOR(31 downto 0)
	
	);
	
	end component;
	
	signal CHOSEN_DEVICE : STD_LOGIC; -- 0 for RAM, 1 for UART
	
	signal ACTIVATE_RAM : STD_LOGIC; -- 1 if the address is within the range of MEM	
	signal ACTIVATE_IO : STD_LOGIC;

	-- True data to be written into RAM, after forwarding (consider LW, followed by SW)
	signal WRITE_DATA_TRUE : STD_LOGIC_VECTOR(31 downto 0);

begin

	WRITE_DATA_TRUE <= MEM_WB_DATA_READ_IN 
	when WRITE_DATA_SRC_IN = MEM_WB_READ_TO_IN -- previous operation wrote to a register we want to store in mem 
		and MEM_WB_IS_READ_IN = '1' -- previous operation is a read operation
		and MEM_WB_IS_WRITE_TO_REG_IN  = '1'-- forward from MEM/WB register
		else WRITE_DATA; -- normal case

	--- Instantiate RAM
	RAM_BANK_1 : DATA_RAM
	PORT MAP(
	CLK => CLK,
	DATA_IN => WRITE_DATA_TRUE,
	ADDR_IN => ALU_IN(10 downto 2), -- we do NOT CARE about the lower 2 bits, word addressable
	ENABLE => ACTIVATE_RAM,
	WRITE_ENABLE => MEM_WRITE_IN,
	DATA_OUT => RAM_DATA
	);


	-- Instantiate UART output
	IO : MEM_MAPPED_IO 	
	PORT MAP (
	CLK => CLK,
	
	DATA_IN => WRITE_DATA_TRUE,
	ADDR_IN => ALU_IN(0 downto 0),
	ENABLE => ACTIVATE_IO,
	WRITE_ENABLE => MEM_WRITE_IN,
	RES1 => U_RES1_OUT,
	RES2 => U_RES2_OUT
	
	);

	INPUT:UART_IN
	PORT MAP(
	clk => clk,
	ADDR_IN => ALU_IN(0 downto 0),
	
	-- Input from UART module
	UART_IN1 => U_OP1_IN,
	UART_IN2 => U_OP2_IN,
	
	DATA_OUT => UART_DATA
	
	);
	
	
	MEMORY_OUT <= RAM_DATA when chosen_device = '0' else UART_DATA;
	--- Choose when to activate Memory
	process (ALU_IN, MEM_WRITE_IN, UART_DATA, RAM_DATA)
	begin
		
		-- Activate normal RAM
		if (ALU_IN(31 downto 11) = x"10010" & "0") then
			ACTIVATE_RAM <= '1';
		else
			ACTIVATE_RAM <= '0';
		end if;
		
		-- Activate UART output 
		if (ALU_IN(31 downto 1) = x"0000000" & "001") then
			ACTIVATE_IO <= '1' and MEM_WRITE_IN;
		else
			ACTIVATE_IO <= '0' and MEM_WRITE_IN;
		end if;
		
	end process;

	process (CLK)
	begin		
		if (CLK'event and CLK = '1') then
	
			if (ALU_IN(31 downto 1) = x"0000000" & "010") then
				Chosen_device <= '1';
			else
				chosen_device <= '0';
			end if;
	
			--- Flow through signals
			REG_DEST_OUT <= REG_DEST_IN;
			ALU_OUT <= ALU_IN;
			REG_WRITE_OUT <= REG_WRITE_IN;
			MEM_TO_REG_OUT <= MEM_TO_REG_IN;
						
		end if;
	end process;

end Behavioral;

